﻿// -----------------------------------------------------------------------
// <copyright file="Helper.cs" company="Microsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace SpaceshipDuel
{
    using System;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;

    /// <summary>
    /// Hilfsklasse zur Erkennung von Kollisionen und Berechnung Elastischer Stöße
    /// auf Basis von Bounding Circles
    /// </summary>
    public static class CollisionHelper
    {
        public static void ElasticCollision(Vector2 center1, Vector2 center2, Vector2 velocity1, Vector2 velocity2, int worldSize, ref Vector2 result1, ref Vector2 result2)
        {
            // Based on the following code:
            // http://www.entwickler-ecke.de/topic_+Kollisionen+zwischen+Billardkugeln+berechnen_47305,0.html
           
            // Abstandsberechnung
            double dX = center2.X - center1.X;
            double dY = center2.Y - center1.Y;

            if (dX > worldSize / 2)
            {
                if (dX < 0)
                    dX = (worldSize - Math.Abs(dX));
                else
                    dX = -worldSize - dX;
            }

            if (dY > worldSize / 2)
            {
                if (dY < 0)
                    dY = (worldSize - Math.Abs(dY));
                else
                    dY = -worldSize - dY;
            }
              
            double abstand = Math.Sqrt(dX * dX + dY * dY);

            double m11 = dX / abstand;
            double m12 = -dY / abstand;

            double m21 = dY / abstand;
            double m22 = dX / abstand;

            // Koordinatentransformation
            double vp1 = velocity1.X * m11 + velocity1.Y * -m12;
            double vs1 = velocity1.X * -m21 + velocity1.Y * m22;
            double vp2 = velocity2.X * m11 + velocity2.Y * -m12;
            double vs2 = velocity2.X * -m21 + velocity2.Y * m22;

            // Objekte entfernen sich voneinander
            if ((vp1 - vp2) < 0)
            {
                result1 = velocity1;
                result2 = velocity2;
                return;
            }

            // Mit Masseberechnung
            /*
            const double masse1 = 10;
            const double masse2 = 10;
            double mtot = masse1 + masse2;
            double vp1_ = (masse1 - masse2) / mtot * vp1 + 2 * masse2 / mtot * vp2;
            double vp2_ = (masse2 - masse1) / mtot * vp2 + 2 * masse1 / mtot * vp1;
            */

            // Ohne Masseberechnung
            double vp1_ = vp2;
            double vp2_ = vp1;

            double vx1 = vp1_ * m11 + vs1 * m12;
            double vy1 = vp1_ * m21 + vs1 * m22;
            double vx2 = vp2_ * m11 + vs2 * m12;
            double vy2 = vp2_ * m21 + vs2 * m22;

            result1 = new Vector2((float)vx1, (float)vy1) * 2;
            result2 = new Vector2((float)vx2, (float)vy2) * 2;
        }

        /// <summary>
        /// Überprüfung, ob eine Kollision vorliegt, basierend auf Bounding Circles (Mittelpunkt + Radius)
        /// </summary>
        /// <param name="center1">Objekt 1 Mittelpunkt</param>
        /// <param name="radius1">Objekt 1 Radius</param>
        /// <param name="center2">Objekt 2 Mittelpunkt</param>
        /// <param name="radius2">Objekt 2 Radius</param>
        /// <param name="worldSize">Größe des Spielfeldes in Pixeln</param>
        /// <returns>true, wenn eine Kollision vorliegt</returns>
        public static bool Collision(Vector2 center1, float radius1, Vector2 center2, float radius2, int worldSize)
        {
            float distance = radius1 + radius2; // Kollisionsabstand

            double abstand = CollisionHelper.GetDistance(center1, center2, worldSize);

            return abstand <= distance;
        }

        /// <summary>
        /// Pixelgenaue Kollision
        /// http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2D/Coll_Detection_Overview.php
        /// </summary>
        public static bool PixelCollision(Color[,] colors1, Vector2 center1, float rotationAngle1, Color[,] colors2, Vector2 center2, float rotationAngle2)
        {
            int width1 = colors1.GetLength(0);
            int height1 = colors1.GetLength(1);
            int width2 = colors2.GetLength(0);
            int height2 = colors2.GetLength(1);
             
            Matrix matrix1Rotation = Matrix.CreateRotationZ(rotationAngle1);
            Matrix matrix2Rotation = Matrix.CreateRotationZ(rotationAngle2);

            Matrix matrix1Translation = Matrix.CreateTranslation(center1.X, center1.Y, 0);
            Matrix matrix2Translation = Matrix.CreateTranslation(center2.X, center2.Y, 0);

            Matrix matrix1 = Matrix.CreateTranslation(-width1 / 2, -height1 / 2, 0) * matrix1Rotation * matrix1Translation;
            Matrix matrix2 = Matrix.CreateTranslation(-width2 / 2, -height2 / 2, 0) * matrix2Rotation * matrix2Translation;

            Matrix mat1to2 = matrix1 * Matrix.Invert(matrix2);

            for (int x1 = 0; x1 < width1; x1++)
            {
                for (int y1 = 0; y1 < height1; y1++)
                {
                    Vector2 pos1 = new Vector2(x1, y1);
                    Vector2 pos2 = Vector2.Transform(pos1, mat1to2);

                    int x2 = (int)pos2.X;
                    int y2 = (int)pos2.Y;
                    if ((x2 >= 0) && (x2 < width2))
                    {
                        if ((y2 >= 0) && (y2 < height2))
                        {
                            if (colors1[x1, y1].A > 0)
                            {
                                if (colors2[x2, y2].A > 0)
                                {
                                    return true;
                                }
                            }
                        }
                    }
                }
            }

            return false;
        }

        // Entfernung zweier Objekte voneinander in der Spielewelt
        public static float GetDistance(Vector2 obj1Pos, Vector2 obj2Pos, int worldSize)
        {
            return GetDistance(obj1Pos, (int)obj2Pos.X, (int)obj2Pos.Y, worldSize);
        }

        // Entfernung zweier Objekte voneinander in der Spielewelt
        public static float GetDistance(Vector2 obj1Pos, int obj2PosX, int obj2PosY, int worldSize)
        {
            float resultX = Math.Abs(obj1Pos.X - obj2PosX);
            float resultY = Math.Abs(obj1Pos.Y - obj2PosY);

            // Korrektur der Abstände, falls die Objekte sich in 
            // verschiedenen Quadranten des Spielfeldes befinden
            if (resultX > worldSize / 2)
                resultX = worldSize - resultX;

            if (resultY > worldSize / 2)
                resultY = worldSize - resultY;

            return (float)Math.Sqrt(resultX * resultX + resultY * resultY);   // tatsächlicher Abstand
        }

        // Entfernung zweier Objekte voneinander in der Spielewelt
        public static float GetDistance(int obj1PosX, int obj1PosY, int obj2PosX, int obj2PosY, int worldSize)
        {
            float resultX = (float)Math.Abs(obj1PosX - obj2PosX);
            float resultY = (float)Math.Abs(obj1PosY - obj2PosY);

            // Korrektur der Abstände, falls die Objekte sich in 
            // verschiedenen Quadranten des Spielfeldes befinden
            if (resultX > worldSize / 2)
                resultX = worldSize - resultX;

            if (resultY > worldSize / 2)
                resultY = worldSize - resultY;

            return (float)Math.Sqrt(resultX * resultX + resultY * resultY);   // tatsächlicher Abstand
        }

    }
}
